webpack 多文件打包

需求的实现过程

需求 实现方法
标准模块化开发 使用 import 和 require 语法来进行模块化开发;
有异步加载的模块 有异步加载的模块
多页项目,压缩html entry 中配置多页入口;HtmlWebpackPlugin 插件进行多页面配置
将共同引入的模块单独打包出来,减少每次重复加载的代码量 使用插件 CommonsChunkPlugin
清除原来打包文件目录 使用插件 CleanWebpackPlugin
使用 es6、es7 语法 使用 babel 来转义
css处理 使用 style-loader 与 css-loader
图片处理 使用 url-loader 与 url-loader
html内图片处理 使用 html-withimg-loader
第三方库Jquery使用 定义entry入口单独打包,webpack.ProvidePlugin全局暴露
压缩js 使用 UglifyJsPlugin 来压缩
压缩css 使用 OptimizeCssAssetsPlugin插件
使用 es6、es7 语法 使用 babel 来转义
使用 es6、es7 语法 使用 babel 来转义
使用 es6、es7 语法 使用 babel 来转义

由于页面相对简单,暂时没有把webpack的配置文件分开,在webpack4.0以后默认是分开的,也没有使用css预处理less和scss等,这个使用配置相应loader就好。

具体文件内容

文件目录:

文件目录

具体注释写带文件中,按从上往下的顺序列出为(找重点的童鞋可跳过前面移步到最后查看webpack.config.js):

cart.css:

1
2
3
4
5
html{
background-color: red;
color: #fff;
font-size: 20px;
}

index.css:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
html{
background-color: black;
color: #fff;
font-size: 20px;
}
.img{
width: 200px;
height: 200px;
background: url("../img/vuex.png") no-repeat;
background-size: contain;
}
.testImg{
width: 400px;
height: auto;
}
.testImg img{
width: 100%;
display: block;
max-width: 100%;
}

common.js:

1
2
3
4
5
6
7
8
9
10
11
//require js 规范
define('common',function () {
return {
initIndex:function () {
console.log("common init index")
},
initCart: function () {
console.log("common init cart")
}
}
})

index.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import './../css/index.css';

/* 异步加载的方式引用第三方jquery,不能抽取公共部分,导致文件庞大
require(['./common.js','jquery'],(common,$) => {
common.initIndex();
$(function () {
console.log("this is JQuery")
});
})
*/

require(['./common.js'],(common) => {
common.initIndex();
$(function () {
console.log("this is JQuery index")
});
})

cart.js:

1
2
3
4
5
6
7
8
import './../css/cart.css';

require(['./common.js'],(common) => {
common.initCart();
$(function () {
console.log("this is JQuery cart")
});
})

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>我是首页</h1>
<a href="cart.html">购物车</a>
<div class="img"></div>
<h2>测试图片</h2>
<div class="testImg">
<img src="img/vuex.png"/>
</div>
</body>
</html>

cart.html

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>购物车</title>
</head>
<body>
<h1>这是购物车页面</h1>
<a href="index.html">回到首页</a>
</body>
</html>

package.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "webpack.config.js",
"dependencies": {
"jquery": "^3.3.1"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-preset-env": "^1.7.0",
"clean-webpack-plugin": "^0.1.19",
"css-loader": "^1.0.0",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.2.0",
"html-withimg-loader": "^0.1.16",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"webpack": "^3.3.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

webpack.config.js

最重要的 webpack.config.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
var path = require('path');
var webpack = require('webpack');

//打包生成html必要插件
var HtmlWebpackPlugin = require('html-webpack-plugin');
//清除文件目录插件
var CleanWebpackPlugin = require('clean-webpack-plugin');
//在打包文件中提取文本到一个单独的文件中插件
//主要用来将CSS模块放入到一个单独的CSS文件中。
//让的样式不再内联到JS捆绑包中,而是在一个单独的CSS文件中。
//如果样式体积很大,那么它将会更快,因为打包后的CSS包与JS包是并行加载的
var ExtractTextPlugin = require("extract-text-webpack-plugin");
//压缩css插件
var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

// CleanWebpackPlugin 清除的目录
var pathsToClean = [
'./dist'
]

// CleanWebpackPlugin 清除的选项配置
var cleanOptions = {
root: path.join(__dirname,''),
verbose: true, //是否在控制台打印出日志
dry: false //是否启用模拟删除文件
//exclude: ['shared.js'],排除特定文件或目录
}

module.exports = {
entry:{ //入口文件
//vendor为自定义,这里把jquery和commonjs各自单独打包,此外的其它文件的合并一个文件打包
vendor: ['jquery','./src/js/common.js'],
index:'./src/js/index.js',
cart:'./src/js/cart.js'
},
output:{ //输出文件
path: path.join(__dirname,'./dist'),//用path.join来把相对路径改为绝对路径
filename:'js/[name].js', //[name]动态打包文件,根据入口文件名key不同打包输出不同文件
publicPath: '' //例如 http://cloud.xx.com/ ,对项目中打包后的js文件前面加上特定的路径(cdn)
},
module:{//loader模块
rules:[
{
test:/\.js$/, //ES6解析转换
include: path.join(__dirname,'src'), //指定特定打包目录
exclude: /node_modules/, //排除特定目录
//虽然这里只使用了babel-loader,但是需要同时安装babel-core与babel-preset-env,
//并且还需要配置同级目录文件.babelrc
loader: 'babel-loader'
},
// {
// test: /\.css$/,
// use: ExtractTextPlugin.extract({
// fallback: 'style-loader',
// use: [
// {
// loader: 'css-loader',
// options: {
// minimize: true //我用这种方式压缩一直是失败的,原因暂时未知
// }
// }
// ]
// })
// }

{
test:/\.css$/, //css解析
include: path.join(__dirname,'src'), //指定特定打包目录
exclude: /node_modules/, //排除特定目录
//loader: 'style-loader!css-loader', //通过style-loader方式,css会直接插入到页面,不增加请求次数
use: ExtractTextPlugin.extract({ //通过ExtractTextPlugin抽取文本,这样CSS会多一次请求,可压缩
fallback: "style-loader",//使用的loader的顺序
use: 'css-loader'
})
},
{
test: /\.(png|jpg|jpe?g|gif|svg)$/, //图片解析
use: [
{
loader: 'url-loader', //还需要安装file-loader
options: {
limit: 100000,
name: './img/[hash].[ext]'
}
}
]
},
{
test: /\.html$/,//html内图片处理
use: [
{
loader: 'html-withimg-loader',
}
]
}
]
},
plugins:[ //插件
new CleanWebpackPlugin(pathsToClean,cleanOptions),
new webpack.ProvidePlugin({ //jQuery的JS文件加载之后不能直接使用,需要如下配置
jQuery: "jquery",
$: "jquery"
}),
new webpack.optimize.CommonsChunkPlugin({ //webpack自带优化插件,抽取公共模块
name: 'vendor', //昵称name,是提取公共代码块后js文件的名字
chunks: ['index','cart','vendor'], //对数组中的js的公共模块进行抽取(从入口entry文件中选择需要抽取的)
mikChunks: 3 //指定最小分割文件,需要三个文件都有公用部分
}),
new webpack.optimize.UglifyJsPlugin({ //webpack自带压缩js插件
compress:{
warnings: false //指定警告是否压缩进去
}
}),
new HtmlWebpackPlugin({
filename: 'index.html', //入口文件
template: './src/index.html', //模板
chunks:['index','vendor'], //该html内引用的 chunks js文件
minify:{
caseSensitive: false, //是否大小写敏感
removeComments:true, // 去除注释
removeEmptyAttributes:true, // 去除空属性
collapseWhitespace: true //是否去除空格
}
}),
new HtmlWebpackPlugin({
filename: 'cart.html',
template: './src/cart.html',
chunks:['cart','vendor'],
minify:{
caseSensitive: false, //是否大小写敏感
removeComments:true, // 去除注释
removeEmptyAttributes:true, // 去除空属性
collapseWhitespace: true //是否去除空格
}
}),
new ExtractTextPlugin('[name].css',{//定义打包后生成的样式文件名称
// filename: (getPath) => {//也可以利用函数返回进行多css文件打包
// return getPath('[name].css');
// },
allChunks: true //是否使用chunk
}),
new OptimizeCssAssetsPlugin({//压缩css
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'),
cssProcessorOptions: { safe: true, discardComments: { removeAll: true } },
canPrint: true
})
],
devtool: '#source-map' //对打包的文件生成一份source-map文件进行输出用来调试,设置false为取消
}

总结: 以上为一个多页面项目打包的配置方式,暂时能想到的为上面的配置,在这里记录一下。当然在实际开发中根据不同的项目还有很多不同的配置,而且上面用到的不管是插件还是配置都是相对常用的简单的几个,更多使用方式应去寻找具体的官网文档进行查看。

这里额外提一下,webpack打包时异步加载js的方式,类似于common.js和requirejs的加载方式:

文件目录

插件使用

在项目中使用插件是不可避免的,插件基本可以分为两种:

一、

如果插件包是纯粹的JS、CSS文件,并且暴露了全局插件变量,则只要import ‘xxxxxx’就可以了,其中xxxxxx是JS或者CSS的路径。此类插件如jQuery和Bootstrap。

但是需要注意,jQuery的JS文件经过import ‘….’加载之后不能直接使用,需要在打包脚本配置webpack.base.conf.js中添加如下配置(部分配置省略):

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = {
entry: { ... },
output: { ... },
resolve: { ... },
...
plugins: [
new webpack.ProvidePlugin({
jQuery: "jquery",
$: "jquery"
})
]
};

如果是插件已经模块化了,暴露了对象或者方法,但是无法跨模块使用(比如通过npm安装到项目的bootbox插件),我是通过在框架App.vue里添加import bootbox from ‘bootbox/bootbox.js’,然后在App.vue的data中添加一行bootbox: bootbox,在其他Vue页面中通过this.$root.bootbox调用bootbox插件的。

二、

还有一种插件,不能通过import的方法加载,需要使用var xxx = require(‘xxx’)的方式加载。这种插件一般是作为Vue框架的插件来用的,比如Vue-filter。这种一般是在main.js里添加var vueFilter = require(‘vue-filter’)加载,然后用Vue.use(vueFilter)使vue-filter在Vue应用中注册。



完~